home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Fritz: All Fritz
/
All Fritz.zip
/
All Fritz
/
FILES
/
PROGBLER
/
WHIZZARD.LZH
/
PRSLASHO.ASM
< prev
next >
Wrap
Assembly Source File
|
1984-06-26
|
23KB
|
960 lines
COMMENT *
CLUBware (tm)
PRSLASHO - Enhancement to the Basic Compiler Print statement
(for routines compiled with /O option)
Copyright 1984 Rayhawk Automation N.W. Inc
P.O. Box 1427
Beaverton, Oregon 97075
*
SCRDSEG SEGMENT
ASSUME CS:SCRDSEG,DS:NOTHING,ES:NOTHING,SS:STACK
ORIG_INT10 LABEL WORD ; original int 10
DW 0
DW 0
;______________________________________________________________________________
; Swap the code used by BASIC to print strings. Swap must be made
; after BASIC has been loaded and initialized its environment. When the
; screen is cleared INTSWAP intercepts the BASIC print statement.
ORIG_SUB LABEL WORD ; original subroutine for printing
DW 0 ; strings
BASIC_SEGMENT LABEL WORD
DW 0
FLAG_ADDRESS LABEL WORD ; address of flag used to determine
DW 0 ; target of i/o for basic
SWAPFLAG LABEL BYTE
DB 00000000b ; flag controlling swapping of subs
TRAP_CHAR EQU 00000001b ; trap next character to be printed
SECOND_TIME EQU 00000010b ; second character printed
UNDER_DEBUG EQU 00000100b ; executing under debug
SCREEN_PAGE LABEL WORD
DW 0 ; address of current page
SCREEN_POSITION LABEL WORD
DW 0 ; address of screen position
BYTES_LINE LABEL WORD
DW 0 ; address of number of bytes per line
CHAR_ATTRIBUTE LABEL WORD
DW 0 ; address of attribute
CALLERS_SEG EQU WORD PTR [BP+4] ; location of callers segment on stack
STRING_CALLER EQU WORD PTR [BP+16] ; location of callers offset on stack
NUMBER_CALLER EQU WORD PTR [BP+26] ; location of callers offset on stack
INTSWAP PROC FAR
CMP AX,0600h ; clear screen request?
JNE NOT_SCREEN_CLEAR
MOV SWAPFLAG,TRAP_CHAR ; try trapping the next char printed
JMP DWORD PTR ORIG_INT10 ; pass call to BIOS
NOT_SCREEN_CLEAR:
TEST SWAPFLAG,TRAP_CHAR ; supposed to trap next char?
JZ NOT_TRAPPED
CMP AH,09 ; request to print a char?
JNE NOT_TRAPPED
TEST SWAPFLAG,SECOND_TIME ; is this the second char printed?
JNZ DO_SWAP
OR SWAPFLAG,SECOND_TIME ; first character has been seen
NOT_TRAPPED:
JMP DWORD PTR ORIG_INT10 ; pass call to BIOS
DO_SWAP:
TEST SWAPFLAG,UNDER_DEBUG
JNZ NOT_TRAPPED
OR SWAPFLAG,UNDER_DEBUG
PUSH BP ; address the stack
MOV BP,SP
PUSH DS
PUSH SI
PUSH AX
MOV AX,CALLERS_SEG ; address basic's code space
MOV DS,AX
MOV SI,NUMBER_CALLER ; load offset of intermediate caller
SUB SI,8 ; back up to start of LOOP
; do we recognize this as a number being printed?
CMP WORD PTR DS:[SI],0AACh ; code to print number?
JNE MAYBE_STRING ; jump if so
CMP BYTE PTR DS:[SI+8],0EBh ; just to make sure
JE GET_SUB_ADDRESS
JMP ALREADY_SAVED
; see if perhaps this is a string being printed
MAYBE_STRING:
CMP WORD PTR DS:[SI+4],0E8ACh ; code to print string?
JNE ALREADY_SAVED ; jump if not
CMP BYTE PTR DS:[SI+8],0E2h ; just to make sure
JNE ALREADY_SAVED
GET_SUB_ADDRESS:
ADD SI,WORD PTR DS:[SI+6] ; load offset of sub
ADD SI,8
; have we already taken over this subroutine once before?
CMP WORD PTR DS:[SI+1],OFFSET PRINTER
JE ALREADY_SAVED
CMP BYTE PTR DS:[SI],9Ah ; opcode is far call?
JE SECOND_COPY
; track down the data we need to perform screen output
PUSH SI
PUSH AX
MOV AX,WORD PTR DS:[SI+3] ; load offset of flag
MOV FLAG_ADDRESS,AX ; save it for later
ADD SI,WORD PTR DS:[SI+11] ; code where screen position is used
ADD SI,60h
ADD SI,WORD PTR DS:[SI] ; code where screen position is used
ADD SI,8
MOV AX,WORD PTR DS:[SI]
MOV SCREEN_POSITION,AX
ADD SI,23h ; code where bytes/line is used
MOV AX,WORD PTR DS:[SI]
MOV BYTES_LINE,AX
ADD SI,5Eh ; code where attribute is used
MOV AX,WORD PTR DS:[SI]
MOV CHAR_ATTRIBUTE,AX
SUB SI,5 ; back up to where new position is called
ADD SI,WORD PTR DS:[SI] ; add offset of subroutine
ADD SI,11 ; forward to where next sub is called
ADD SI,WORD PTR DS:[SI] ; add offset of subroutine
ADD SI,5 ; code where page is used
MOV AX,WORD PTR DS:[SI]
MOV SCREEN_PAGE,AX
POP AX
POP SI
; replace the original code with a call to PRINTER to intercept prints
SECOND_COPY:
MOV BYTE PTR DS:[SI],9Ah ; opcode is far call
MOV WORD PTR DS:[SI+1],OFFSET PRINTER ; target for call
MOV WORD PTR DS:[SI+3],SEG PRINTER
MOV BASIC_SEGMENT,AX ; save basic's code seg
ADD SI,5 ; walk past call
MOV ORIG_SUB,SI ; save original sub addr
ALREADY_SAVED:
MOV SWAPFLAG,0 ; clear the swap flag
POP AX
POP SI
POP DS
POP BP
JMP DWORD PTR ORIG_INT10 ; pass clear screen to BIOS now
INTSWAP ENDP
;______________________________________________________________________________
COMMENT * PRINTER prints a character string on the screen starting
at the current cursor position. After the string is
written to the screen the cursor position is updated
to just after the string.
To use this routine, a clear screen must occur
to swap in the PRINTER routine for basic.
The PRSLASHO module will take over the INT 10h and make
this module permanently resident. The user from inside
his BASIC program will clear the screen with a CLS
which will perform the vector swap.
Algorithm:
On entry
DS:[SI] points to string to write
CX contains count of characters to write
BX may contain count of blanks to write
SCREEN_PAGE contains the page to which we write
CHAR_ATTRIBUTE contains the attribute for the text
SCREEN_POSITION contains the current screen location and must
be updated after print is complete
*
;______________________________________________________________________________
; Local data addressable through CS register
CHAR_COUNT LABEL WORD
DW 0
LINE_START LABEL WORD
DW 0 ; 0
DW 160 ; 1
DW 320 ; 2
DW 480 ; 3
DW 640 ; 4
DW 800 ; 5
DW 960 ; 6
DW 1120 ; 7
DW 1280 ; 8
DW 1440 ; 9
DW 1600 ; 10
DW 1760 ; 11
DW 1920 ; 12
DW 2080 ; 13
DW 2240 ; 14
DW 2400 ; 15
DW 2560 ; 16
DW 2720 ; 17
DW 2880 ; 18
DW 3040 ; 19
DW 3200 ; 20
DW 3360 ; 21
DW 3520 ; 22
DW 3680 ; 23
DW 3840 ; 24
PAGE_START LABEL WORD
DW 0
DW 4000
DW 8000
DW 12000
STARTING_PAGE LABEL WORD
DW 0
END_OF_PAGE LABEL WORD
DW 0
STARTING_LINE LABEL WORD
DW 0
RESIDUAL LABEL WORD
DW 0
LOCAL_FLAG LABEL BYTE
DB 00000000b
JUST_BLANKS EQU 00000001b ; basic wants just blanks displayed
ZERO_TAGGED EQU 00000010b ; string length unknown, tagged by 0
;______________________________________________________________________________
COMMENT *
New PRINT service routine for BASIC
Calls to routine XXXX:0C5B are rerouted to this piece of code
These calls come from several places within the BASIC rutime library
We service calls from four locations: - calls to display a string
- calls to expand tabs
- calls to blank pad fields
- calls to display a number
Calls from anywhere else are serviced by the original basic code.
The four labels corresponding to the four types of calls we service
are STRING_IO
REPEAT_BLANKS
NUMBER_STRING
PAD_FIELD
The code labeled BASIC_IO passes the interrupt back to the basic code
for service.
*
PRINTER PROC FAR
PUSH BP
PUSH ES
MOV BP,BASIC_SEGMENT
MOV ES,BP
MOV LOCAL_FLAG,0 ; clear the flag for string i/o
; check context for simple text to screen or possibly something
; more involved that basic should handle
MOV BP,FLAG_ADDRESS
CMP WORD PTR DS:[BP],0
JNE BASIC_IO
MOV BP,BYTES_LINE
CMP BYTE PTR DS:[BP],50h ; 80 bytes per line?
JNE BASIC_IO
MOV BP,SP ; address the stack
MOV BP,WORD PTR [BP+8] ; load offset of caller
CMP BYTE PTR ES:[BP],0EBh ; number to print?
JE NUMBER_STRING
CMP BYTE PTR ES:[BP],04Bh ; tab to expand?
JE REPEAT_BLANKS
CMP BYTE PTR ES:[BP],0E2h ; is this a loop instruction?
JNE BASIC_IO
CMP BYTE PTR ES:[BP-4],0ACh ; is this LODSB in loop?
JE STRING_IO
CMP WORD PTR ES:[BP-5],20B0h ; is this MOV AL in loop?
JE PAD_FIELD
; allow original service routine to perform I/O
BASIC_IO:
MOV BP,SP
PUSH AX
MOV AX,WORD PTR [BP+8]
POP AX
POP ES
POP BP
ADD SP,4 ; clear our far call from stack
PUSH SI ; original first two statements
MOV SI,FLAG_ADDRESS
MOV SI,WORD PTR DS:[SI] ; of the basic subroutine
JMP DWORD PTR ORIG_SUB ; pass control to original routine
; ----------
; send a number string to screen
; string length is not known but end is tagged by a zero
NUMBER_STRING:
PUSH BX ; save registers used
PUSH DX
PUSH DI
; ... 1) store character count and mark the flag for type of i/o
DEC SI ; backup to start of string
MOV CHAR_COUNT,80 ; always less than 80 characters
OR LOCAL_FLAG,ZERO_TAGGED
MOV RESIDUAL,CX ; restore original cx contents when done
JMP SETUP_START
; ----------
; repeated blanks sent to screen to expand a tab
REPEAT_BLANKS:
PUSH BX ; save registers used
PUSH DX
PUSH DI
; ... 1) store character count and mark the flag for type of i/o
MOV CHAR_COUNT,BX ; save character count
OR LOCAL_FLAG,JUST_BLANKS
MOV RESIDUAL,1 ; leave one in cx register when done
JMP SETUP_START
; ----------
; repeated blanks sent to screen to fill a field area
PAD_FIELD:
PUSH BX ; save registers used
PUSH DX
PUSH DI
; ... 1) store character count and mark the flag for type of i/o
MOV CHAR_COUNT,CX ; save character count
OR LOCAL_FLAG,JUST_BLANKS
MOV RESIDUAL,1 ; leave one in cx register when done
JMP SETUP_START
; ----------
; write the string for basic
STRING_IO:
; CMP BYTE PTR DS:[06CAh],50h ; 80 bytes per line?
; WAS ORIGINALLY 6D0h
; JNE BASIC_IO
PUSH BX ; save registers used,
PUSH DX
PUSH DI
; ... 1) backup to start of string and store character count
DEC SI ; backup to start of string
MOV CHAR_COUNT,CX ; save character count
MOV RESIDUAL,1 ; leave one in cx register when done
; ... 2) load current page
SETUP_START:
MOV BP,SCREEN_PAGE
MOV BL,BYTE PTR DS:[BP] ; load current page
SUB BH,BH
SHL BX,1 ; convert to a table offset
MOV DI,PAGE_START[BX] ; load start of page
MOV STARTING_PAGE,DI ; save the page
MOV END_OF_PAGE,DI ; save ending page pointer
ADD END_OF_PAGE,4000
; ... 3) load current position on page
MOV BP,SCREEN_POSITION
MOV DX,WORD PTR DS:[BP] ; load current position
DEC DH ; basic counts from 1 instead of 0
DEC DL ; basic counts from 1 instead of 0
MOV BL,DL ; load row number
SHL BX,1 ; two bytes per table entry
ADD DI,LINE_START[BX] ; add in start of line
MOV STARTING_LINE,DI ; store this for later
MOV DL,DH ; bring down column number
SUB DH,DH
ADD DI,DX ; add in column position
ADD DI,DX ; account for attribute bytes
; ... 4) load attribute for string
MOV BP,CHAR_ATTRIBUTE
MOV BH,BYTE PTR DS:[BP] ; load attribute for string
; ... 5) move character count to CX for use in loop
MOV CX,CHAR_COUNT ; load the character count
; ... 6) load type of crt display from 0000:463
SUB AX,AX ; address system memory
MOV ES,AX
MOV DX,WORD PTR ES:[463h] ; load address of display adapter
ADD DX,6 ; address crt status port
; ... 7) address the screen segment
MOV AX,0B000h ; screen seg for monochrome card
CMP DX,03DAh ; is this a graphic card?
JNE MONOCHROME
MOV AX,0B800h ; load screen seg for graphic card
MONOCHROME:
MOV ES,AX ; address the screen buffer
; ... 8) move string to screen while synchronizing
; with horizontal retrace
TEST LOCAL_FLAG,JUST_BLANKS
JZ DISPLAY_LOOP
MOV BL,20h ; load a blank
BLANK_LOOP:
CALL DISPLAY_CHAR ; display a line of blanks for basic
LOOP BLANK_LOOP
JMP UPDATE_POSITION
DISPLAY_LOOP:
LODSB ; load next character
CMP AL,20h ; special character?
JGE NOT_SPECIAL
CALL SPECIAL
JZ ANOTHER_CHAR ; if flag set, go for another character
NOT_SPECIAL:
MOV BL,AL
CLI
HSYNC_WAIT1:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JNZ HSYNC_WAIT1 ; wait for retrace
HSYNC_WAIT2:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JZ HSYNC_WAIT2 ; wait for retrace
MOV AX,BX
STOSW ; store character and attribute
ANOTHER_CHAR:
STI
CMP DI,END_OF_PAGE
JL NOT_SCROLLED
MOV AL,0Dh ; force a carriage return and scroll
CALL SPECIAL
NOT_SCROLLED:
LOOP DISPLAY_LOOP ; repeat cx times
; ... 9) update cursor position on screen
UPDATE_POSITION:
MOV AX,DI
SUB AX,STARTING_PAGE
SHR AX,1 ; eliminate attribute bytes
SUB DX,DX
MOV BX,80 ; divide by bytes per line
DIV BX ; quotient in AL (ROW)
; remainder in DL (COLUMN)
MOV DH,AL
MOV BP,SCREEN_PAGE
MOV BH,BYTE PTR DS:[BP] ; load page number
MOV AH,2 ; request new position
INT 10h
; ... 10) update cursor position
XCHG DH,DL ; basic likes this reversed
INC DH ; basic counts from 1 instead of 0
INC DL ; basic counts from 1 instead of 0
MOV BP,SCREEN_POSITION
MOV WORD PTR DS:[BP],DX
; ... 11) leave registers in manner BASIC expects
MOV CX,RESIDUAL ; let decrement instr take this to zero
; inside basic
POP DI
POP DX
POP BX
POP ES
POP BP
ADD SP,4 ; throw away offset and code segment
; from our call
POP AX ; throw away near call on stack
PUSH BASIC_SEGMENT ; and convert to a far call
PUSH AX
RET ; return to basic
PRINTER ENDP
;______________________________________________________________________________
; subroutine to handle special control characters
SPECIAL PROC NEAR
; ----------
CMP AL,0Ah ; line feed?
JE NEW_LINE
; ----------
CMP AL,0Bh ; home?
JNE NOT_HOME
MOV DI,STARTING_PAGE ; start over at top of screen
MOV STARTING_LINE,DI
SUB AL,AL
RET
NOT_HOME:
; ----------
CMP AL,0Ch ; clear screen?
JNE NOT_CLEAR
MOV DI,STARTING_PAGE
MOV STARTING_LINE,DI
MOV AL,0 ; clear whole window
JMP SHORT SCROLL_SCREEN
NOT_CLEAR:
; ----------
CMP AL,0Dh ; carriage return?
JNE NOT_CR
NEW_LINE:
MOV DI,STARTING_LINE
ADD DI,160
JMP SHORT TEST_RIGHT
NOT_CR:
; ----------
CMP AL,1Ch ; move right?
JNE NOT_RIGHT
ADD DI,2
JMP SHORT TEST_RIGHT
NOT_RIGHT:
; ----------
CMP AL,1Dh ; move left?
JNE NOT_LEFT
SUB DI,2
JMP SHORT TEST_LEFT
NOT_LEFT:
; ----------
CMP AL,1Eh ; move up?
JNE NOT_UP
SUB DI,160
JMP SHORT TEST_LEFT
NOT_UP:
; ----------
CMP AL,1Fh ; move down?
JNE NOT_DOWN
ADD DI,160
JMP SHORT TEST_RIGHT
NOT_DOWN:
JMP SHORT TEST_FOR_TAB
; ----------
TEST_RIGHT:
CMP DI,END_OF_PAGE ; are we past line 25?
JL VALID_RIGHT
MOV DI,STARTING_PAGE ; back at start of last line
ADD DI,3840
MOV STARTING_LINE,DI
MOV AL,1 ; scroll one line
JMP SHORT SCROLL_SCREEN
VALID_RIGHT:
SUB AL,AL
RET
; ----------
SCROLL_SCREEN:
PUSH CX
MOV CX,0 ; start in upper left corner
PUSH DX
MOV DX,184Fh ; end in lower right
MOV AH,6
PUSHF ; simulate an INT 10
CALL DWORD PTR ORIG_INT10
POP DX
POP CX
SUB AL,AL
RET
; ----------
TEST_LEFT:
CMP DI,STARTING_PAGE
JGE VALID_LEFT
MOV DI,STARTING_PAGE
VALID_LEFT:
SUB AL,AL
RET
; ----------
TEST_FOR_TAB:
CMP AL,09h ; tab?
JNE NOT_TAB
PUSH CX
PUSH DX
MOV AX,DI
SUB AX,STARTING_LINE
SHR AX,1 ; discount attribute bytes
SUB DX,DX
MOV CX,8
DIV CX
MOV CX,8 ; tab positions are every 8 charactes
SUB CX,DX ; subtract off remainder
MOV BL,' ' ; write some blanks
POP DX
TAB_LOOP:
CALL DISPLAY_CHAR
LOOP TAB_LOOP
POP CX
SUB AL,AL
RET
NOT_TAB:
; ----------
CMP AL,08h ; backspace?
JNE NOT_BACKSPACE
CMP DI,STARTING_LINE
JE AT_START
SUB DI,2 ; back up a space
AT_START:
MOV BL,' ' ; write a blank
CALL DISPLAY_CHAR
SUB DI,2 ; back up a space
SUB AL,AL
RET
NOT_BACKSPACE:
; ----------
CMP AL,00h ; null?
JNE NOT_NULL
TEST LOCAL_FLAG,ZERO_TAGGED ; does this mean end of string?
JZ NOT_NULL ; branch if null meaningless
MOV CX,1 ; terminate the display loop
DEC SI ; backup so basic can see null too
SUB AL,AL
RET
NOT_NULL:
; ----------
MOV AH,2
SUB AH,1 ; set flag to display the char
RET
SPECIAL ENDP
;______________________________________________________________________________
; routine to display a character in BL - used only for special characters
; and repeated blanks - attribute is in BH
DISPLAY_CHAR PROC NEAR
CLI
HSYNC_WAIT3:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JNZ HSYNC_WAIT3 ; wait for retrace
HSYNC_WAIT4:
IN AL,DX ; check for horizontal retrace
TEST AL,1
JZ HSYNC_WAIT4 ; wait for retrace
MOV AX,BX
STOSW ; store character and attribute
STI
RET
DISPLAY_CHAR ENDP
;______________________________________________________________________________
LASTADDR LABEL BYTE
COPYRIGHT LABEL BYTE
DB 10,13
DB ' CLUBware (tm)',10,13,10,13
DB 'PRSLASHO - Enhancement to the Basic Compiler Print statement'
DB 10,13
DB ' (for routines compiled with /O option)'
DB 10,13,10,13
DB ' Copyright 1984 Rayhawk Automation N.W. Inc',10,13
DB ' P.O. Box 1427',10,13
DB ' Beaverton, Oregon 97075',10,13,'$'
;______________________________________________________________________________
PRSLASHO PROC FAR
PUSH DS ; Push addr of Program Segment Prefix
SUB AX,AX ; Zero AX
PUSH AX ; Push zero onto stack
; (offset of INT 20 within PSP)
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | take over the INT 10h |
; | interrupt if not already done |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV DS,AX ; address low memory
; CMP WORD PTR DS:[40h],OFFSET INTSWAP
; JE ALREADY_DONE
MOV AX,WORD PTR DS:[40h] ; save original int10
MOV ORIG_INT10,AX
MOV AX,WORD PTR DS:[42h]
MOV ORIG_INT10+2,AX
MOV AX,SEG PRSLASHO
MOV DS,AX
MOV DX,OFFSET INTSWAP ; Load offset of interrupt service mod
MOV AX,2510h ; Prepare for DOS service call type 25
; to establish service for INT 10
INT 21h ; Ask DOS to establish service
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | issue copyright message |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV DX,OFFSET COPYRIGHT
MOV AH,9
INT 21h
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | modify INT 20 into INT 27 in the |
; | program segment prefix |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV BYTE PTR ES:[01],27h ; Change INT 20h to INT 27h
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | 6) load address of ending tag into DX |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
MOV AX,SEG LASTADDR
SUB AX,SEG PRSLASHO
MOV CL,4 ; prepare for 4 bit shift
SHL AX,CL ; shift up (convert from seg to abs)
ADD AX,OFFSET LASTADDR ; add address of bottom location
ADD AX,0103h ; Pad offset because DOS measures
; offset relative to Program
; Segment Prefix
MOV DX,AX ; leave where DOS will find it
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
; | |
; | 7) use RET FAR to return to DOS and |
; | leave service routine resident |
; | |
; | - - - - - - - - - - - - - - - - - - - - - - - - -|
ALREADY_DONE:
RET
PRSLASHO ENDP
SCRDSEG ENDS
;______________________________________________________________________________
STACK SEGMENT PARA STACK 'STACK'
DB 24 DUP('STACK***')
TOPSTACK DB 0
STACK ENDS
END PRSLASHO